package com.xyyy.xyplayer.player;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.text.TextUtils;

import com.xyyy.xyplayer.XYTimeInfoBean;
import com.xyyy.xyplayer.listener.XYOnCompleteListener;
import com.xyyy.xyplayer.listener.XYOnErrorListener;
import com.xyyy.xyplayer.listener.XYOnLoadListener;
import com.xyyy.xyplayer.listener.XYOnPauseResumeListener;
import com.xyyy.xyplayer.listener.XYOnPcmInfoListener;
import com.xyyy.xyplayer.listener.XYOnPreparedListener;
import com.xyyy.xyplayer.listener.XYOnRecordTimeListener;
import com.xyyy.xyplayer.listener.XYOnTimeInfoListener;
import com.xyyy.xyplayer.listener.XYOnValumeDBListener;
import com.xyyy.xyplayer.log.LogUtil;
import com.xyyy.xyplayer.muteenum.MuteEnum;
import com.xyyy.xyplayer.utils.ThreadManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;


public class XYplayer {

    static {
        System.loadLibrary("native-lib");
        System.loadLibrary("avcodec");
        System.loadLibrary("avdevice");
        System.loadLibrary("avfilter");
        System.loadLibrary("avformat");
        System.loadLibrary("avutil");
        System.loadLibrary("postproc");
        System.loadLibrary("swresample");
        System.loadLibrary("swscale");
    }

    private static String source;
    private static boolean playNext = false;
    private static int duration = -1;
    private static int volumePercent = 100;
    private static MuteEnum muteEnum = MuteEnum.MUTE_CENTER;
    private static float speed = 1.0f;
    private static float pitch = 1.0f;
    private static boolean initmediacodec = false;//是否初始化过mediacodec

    private static XYplayer xYplayer;

    private XYOnPreparedListener xyOnPreparedListener;

    private XYOnLoadListener xyOnLoadListener;

    private XYOnPauseResumeListener xyOnPauseResumeListener;
    private XYOnTimeInfoListener xyOnTimeInfoListener;
    private XYOnErrorListener xyOnErrorListener;
    private XYOnCompleteListener xyOnCompleteListener;
    private XYOnValumeDBListener xyOnValumeDBListener;
    private XYOnRecordTimeListener xyOnRecordTimeListener;
    private XYOnPcmInfoListener xyOnPcmInfoListener;

    //static 多线程
    private static XYTimeInfoBean xyTimeInfoBean;

    public XYplayer() {
    }

    public static XYplayer getInstance() {
        if (xYplayer == null) {
            xYplayer = new XYplayer();

        }
        return xYplayer;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public void setXyOnPreparedListener(XYOnPreparedListener xyOnPreparedListener) {
        this.xyOnPreparedListener = xyOnPreparedListener;
    }

    public void setXyOnLoadListener(XYOnLoadListener xyOnLoadListener) {
        this.xyOnLoadListener = xyOnLoadListener;
    }

    public void setXyOnPauseResumeListener(XYOnPauseResumeListener xyOnPauseResumeListener) {
        this.xyOnPauseResumeListener = xyOnPauseResumeListener;
    }

    public void setXyOnTimeInfoListener(XYOnTimeInfoListener xyOnTimeInfoListener) {
        this.xyOnTimeInfoListener = xyOnTimeInfoListener;
    }

    public void setXyOnErrorListener(XYOnErrorListener xyOnErrorListener) {
        this.xyOnErrorListener = xyOnErrorListener;
    }

    public void setXyOnCompleteListener(XYOnCompleteListener xyOnCompleteListener) {
        this.xyOnCompleteListener = xyOnCompleteListener;
    }

    public void setXyOnValumeDBListener(XYOnValumeDBListener xyOnValumeDBListener) {
        this.xyOnValumeDBListener = xyOnValumeDBListener;
    }

    public void setXyOnPcmInfoListener(XYOnPcmInfoListener xyOnPcmInfoListener) {
        this.xyOnPcmInfoListener = xyOnPcmInfoListener;
    }

    public void setXyOnRecordTimeListener(XYOnRecordTimeListener xyOnRecordTimeListener) {
        this.xyOnRecordTimeListener = xyOnRecordTimeListener;
    }

    /**
     * 准备播放
     */
    public void prepared() {
        if (TextUtils.isEmpty(source)) {
            LogUtil.d("sourc no be empty");
            return;
        } else {
            LogUtil.d("准备播放 url = " + source);
            ThreadManager.getNormalPool().execute(new Runnable() {
                @Override
                public void run() {
                    n_parpared(source);
                }
            });
        }

    }

    /**
     * 开始播放
     */
    public void start() {
        if (TextUtils.isEmpty(source)) {
            LogUtil.d("source is empty");
            return;
        } else {
            LogUtil.d("开始播放");
            ThreadManager.getNormalPool().execute(new Runnable() {
                @Override
                public void run() {

                    setVolume(volumePercent);
                    setMute(muteEnum);
                    LogUtil.d("初始声道 = " + muteEnum.getValue());
                    setPitch(pitch);
                    setSpeed(speed);
                    n_start();
                }
            });
        }


    }

    //暂停
    public void pause() {
        n_pause();
        if (xyOnPauseResumeListener != null) {
            xyOnPauseResumeListener.onPause(true);
        }
    }

    //播放
    public void resume() {
        n_resume();
        if (xyOnPauseResumeListener != null) {
            xyOnPauseResumeListener.onPause(false);
        }
    }

    //停止
    public void stop() {
        xyTimeInfoBean = null;
        duration = -1;//停止后设置为初始值

        stopRecord();

        //因为stop里面有个while循环 这里开个子线程
        ThreadManager.getNormalPool().execute(new Runnable() {
            @Override
            public void run() {
                LogUtil.d("调用库stop");
                n_stop();
            }
        });
    }

    //seek
    public void seek(int secds) {
        n_seek(secds);
    }

    /**
     * 播放下一个
     *
     * @param url
     */
    public void playNext(String url) {
        source = url;
        playNext = true;
        stop();//停止之后回调
    }

    /**
     * c++回调java的方法  准备的回调
     */
    public void onCallParpared() {
        if (xyOnPreparedListener != null) {
            LogUtil.d("java层回调 准备好了");
            xyOnPreparedListener.onPrepared();
        }
    }

    /**
     * 播放下一个 c++ 调用的
     */
    public void onCallNext() {
        if (playNext) {
            LogUtil.d("被c++调用的java的下一个歌曲");
            playNext = false;
            prepared();
        }
    }

    /**
     * 得到总时长
     *
     * @return
     */
    public int getDuration() {
        if (duration < 0) {
            return n_duration();
        }
        return duration;
    }

    /**
     * 设置音量
     *
     * @param percent
     */
    public void setVolume(int percent) {
        if (percent >= 0 && percent <= 100) {
            volumePercent = percent;
            n_volume(percent);
        }
    }


    public int getVolumePercent() {
        return volumePercent;
    }


    /**
     * 设置声道
     *
     * @param mute
     */
    public void setMute(MuteEnum mute) {
        LogUtil.d("设置声道 = " + mute.getValue());
        muteEnum = mute;
        n_mute(muteEnum.getValue());
    }

    /**
     * 设置声音音调
     *
     * @param p
     */
    public void setPitch(float p) {
        pitch = p;
        n_pitch(pitch);
    }

    /**
     * 设置声音速度
     *
     * @param s
     */
    public void setSpeed(float s) {
        speed = s;
        n_speed(speed);
    }

    /**
     * 开始录制编码
     *
     * @param outfile
     */
    public void startRecord(File outfile) {
        LogUtil.d("开始录制");
        if (!initmediacodec) {
            audioSamplerate = n_samplerate();
            if (audioSamplerate > 0) {
                initmediacodec = true;
                LogUtil.d("n_samplerate() = " + audioSamplerate);
                initMediacodece(audioSamplerate, outfile);
                n_startstoprecord(true);
            }
        }
    }

    /**
     * 停止录制编码 aac
     */
    public void stopRecord() {
        //如果初始化过
        if (initmediacodec) {
            n_startstoprecord(false);
            releaseMediaCodec();
            LogUtil.d("停止录制");
        }
    }

    /**
     * 暂停录制aac
     */
    public void pauseRecord() {
        n_startstoprecord(false);
        LogUtil.d("暂停录制");
    }


    /**
     * 恢复录制aac
     */
    public void resumeRecord() {

        n_startstoprecord(true);
        LogUtil.d("继续录制");
    }

    /**
     * 裁剪音乐
     *
     * @param start_time
     * @param end_time
     * @param showPcm
     */
    public void cutAudioPlay(int start_time, int end_time, boolean showPcm) {
        boolean b = n_cutaudioplay(start_time, end_time, showPcm);
        LogUtil.d("裁剪判断 b = " + b);
        if (b) {
            start();
        } else {
            LogUtil.d("裁剪失败");
            stop();
            onCallError(2001, "cutAudio params is wrong");
        }
    }

    /**
     * 加载的回调
     *
     * @param load
     */
    public void onCallLoad(boolean load) {
        if (xyOnLoadListener != null) {
            xyOnLoadListener.onLoad(load);
        }
    }

    /**
     * 设置播放时间回调
     *
     * @param currentTime
     * @param totalTime
     */
    public void onCallTimeInfo(int currentTime, int totalTime) {
        if (xyOnTimeInfoListener != null) {
            if (xyTimeInfoBean == null) {
                xyTimeInfoBean = new XYTimeInfoBean();
            }
//            LogUtil.d("xyTimeInfoBean = " + xyTimeInfoBean.toString());
            xyTimeInfoBean.setCurrentTime(currentTime);
            xyTimeInfoBean.setTotalTime(totalTime);
            xyOnTimeInfoListener.onTimeInfo(xyTimeInfoBean);
        }
    }

    /**
     * 播放器完成回调
     */
    public void onCallComplete() {
        LogUtil.d("播放完成回调");
        stop();
        if (xyOnCompleteListener != null) {
            xyOnCompleteListener.onComplete();
        }
    }

    /**
     * 播放器出错回调
     *
     * @param code
     * @param msg
     */
    public void onCallError(int code, String msg) {

        if (xyOnErrorListener != null) {
            xyOnErrorListener.onError(code, msg);
        }
    }

    /**
     * 获取音量回调
     *
     * @param db
     */
    public void onCallValumeDB(int db) {
        if (xyOnValumeDBListener != null) {
            xyOnValumeDBListener.onDbValue(db);
        }
    }

    /**
     * 获取pcm数据
     * @param buffer
     * @param buffersize
     */
    public void onCallPcmInfo(byte[] buffer, int buffersize) {
        if (xyOnPcmInfoListener != null) {
            xyOnPcmInfoListener.onPcmInfo(buffer, buffersize);
        }
    }

    /**
     * 回调获取PCM采样率
     * @param samperate
     */
    public void onCallPcmRate(int samperate) {
        if (xyOnPcmInfoListener != null) {
            xyOnPcmInfoListener.onPcmRate(samperate);
        }
    }

    private native void n_parpared(String url);

    private native void n_start();

    private native void n_pause();

    private native void n_resume();

    private native void n_stop();

    private native void n_seek(int secds);

    private native int n_duration();

    private native void n_volume(int percent);

    private native void n_mute(int mute);

    private native void n_pitch(float ptich);

    private native void n_speed(float speed);

    private native int n_samplerate();

    private native void n_startstoprecord(boolean start);

    private native boolean n_cutaudioplay(int start_time, int end_time, boolean showPcm);


    //mediacodece

    private MediaFormat encoderFormat = null;
    private MediaCodec encoder = null;
    //文件的输入流
    private FileOutputStream outputStream = null;
    //存放的是编码或者解码后的信息 比如视频长宽等
    private MediaCodec.BufferInfo info = null;
    //加了7个字节每个头部的数据的大小
    private int perpcmsize = 0;
    private byte[] outByteBuffer = null;
    private int aacsamplerate = 4;//4 根据表 44100
    private double recordTime = 0;//录制的时间
    private int audioSamplerate = 0;//采样率

    //官方文档  https://developer.android.com/reference/android/media/MediaCodec

    /**
     * 编码之后的文件传到outfile 里面
     *
     * @param samperate
     * @param outfile
     */
    private void initMediacodece(int samperate, File outfile) {
        try {
            //拿到aacsamplerate
            aacsamplerate = getADTSsamplerate(samperate);
            //两声道 channelCount 所以是2
            encoderFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, samperate, 2);
            encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//设置码率
            encoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);//设置编码的等级
//            encoderFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 5000);//设置输入最大的值
            encoderFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 4608);//设置输入最大的值

            //编码
            encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);

            info = new MediaCodec.BufferInfo();

            if (encoder == null) {
                LogUtil.e("encoder = null");
                return;
            }

            recordTime = 0;
            //MediaFormat：输入数据（解码器）的格式或输出数据（编码器）的所需格式。通过null 作为format相当于传递一个 an empty mediaformat。
            //surface	Surface：指定要在其上呈现此解码器输出的曲面。传递null就像surface编解码器不生成原始视频输出（例如，不是视频解码器）和/或如果要配置编解码器以进行 ByteBuffer输出。
            //crypto	MediaCrypto：指定加密对象以促进媒体数据的安全解密。通过null作为crypto非安全的编解码器。
            //flags	int：指定CONFIGURE_FLAG_ENCODE将组件配置为编码器。
            encoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            outputStream = new FileOutputStream(outfile);
            encoder.start();

        } catch (IOException e) {
            LogUtil.d("异常了" + e.getMessage());
            e.printStackTrace();
        }
    }


    private void encodecPcmToAAc(int size, byte[] buffer) {
        if (buffer != null && encoder != null) {
//            LogUtil.d("开始  size = "+size);
            //获取队列中当前可用的索引 返回索引值 0 是及时
            int inputBufferindex = encoder.dequeueInputBuffer(0);
//            LogUtil.d("inputBufferindex = "+inputBufferindex+" size = "+size);
            if (inputBufferindex >= 0) {
                if (xyOnRecordTimeListener != null) {
                    //累加  理论上应该的文件大小 / 一秒钟采样率 * 2声道 * 字节(16位 / 8 bit 为2字节)
                    recordTime += size * 1.0 / (audioSamplerate * 2 * (16 / 8));
//                    LogUtil.d("录制时间 = " + recordTime);
                    xyOnRecordTimeListener.onRecordTime((int) recordTime);
                }
                //从对应索引中拿到buffer
                ByteBuffer byteBuffer = encoder.getInputBuffers()[inputBufferindex];
                byteBuffer.clear();
                byteBuffer.put(buffer);
                encoder.queueInputBuffer(inputBufferindex, 0, size, 0, 0);
            }

            int index = encoder.dequeueOutputBuffer(info, 0);
            //如果index>
            while (index >= 0) {
                try {
                    //出队
                    perpcmsize = info.size + 7;
                    outByteBuffer = new byte[perpcmsize];

                    ByteBuffer byteBuffer = encoder.getOutputBuffers()[index];
                    byteBuffer.position(info.offset);
                    byteBuffer.limit(info.offset + info.size);

                    addADTStoPacket(outByteBuffer, perpcmsize, aacsamplerate);
                    //把bytebuffer 从7 位开始放到 info.offset 结束
                    byteBuffer.get(outByteBuffer, 7, info.offset);
                    byteBuffer.position(info.offset);

                    outputStream.write(outByteBuffer, 0, perpcmsize);

                    encoder.releaseOutputBuffer(index, false);

                    //在读取一次index 看还有没有值
                    index = encoder.dequeueOutputBuffer(info, 0);
                    outByteBuffer = null;


                } catch (IOException e) {
                    e.printStackTrace();
                }
            }


        }
    }

    /**
     * 添加ADTS头
     *
     * @param packet
     * @param packetLen
     */
    private void addADTStoPacket(byte[] packet, int packetLen, int samplerate) {
        int profile = 2; // AAC LC
        int freqIdx = samplerate; // 采样率
        int chanCfg = 1; // CPE

        // fill in ADTS data
        packet[0] = (byte) 0xFF; // 0xFFF(12bit) 这里只取了8位，所以还差4位放到下一个里面
        packet[1] = (byte) 0xF9; // 第一个t位放F
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }

    private int getADTSsamplerate(int samplerate) {
        int rate = 4;
        switch (samplerate) {
            case 96000:
                rate = 0;
                break;
            case 88200:
                rate = 1;
                break;
            case 64000:
                rate = 2;
                break;
            case 48000:
                rate = 3;
                break;
            case 44100:
                rate = 4;
                break;
            case 32000:
                rate = 5;
                break;
            case 24000:
                rate = 6;
                break;
            case 22050:
                rate = 7;
                break;
            case 16000:
                rate = 8;
                break;
            case 12000:
                rate = 9;
                break;
            case 11025:
                rate = 10;
                break;
            case 8000:
                rate = 11;
                break;
            case 7350:
                rate = 12;
                break;
        }
        return rate;
    }

    private void releaseMediaCodec() {
        if (encoder == null) {
            return;
        }
        try {
            recordTime = 0;
            outputStream.close();
            outputStream = null;
            encoder.stop();
            encoder.release();
            encoderFormat = null;
            info = null;
            initmediacodec = false;

            LogUtil.d("录制完成");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                outputStream = null;
            }
        }

    }
}
